// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: set discoveryProtocol default to CLIENT
// feature: add multi-domain support for discovery server
// feature: DServer support simple entity
// feature: Support Cache fast application and slow release mechanism
// feature: discovery support client and server
// feature: simplify xml profile
// ------------------------------------------------------------------

#ifndef XMLPARSER_RTPSPARTICIPANTATTRIBUTES_H_
#define XMLPARSER_RTPSPARTICIPANTATTRIBUTES_H_

#include <deps/common/Time_t.h>
#include <deps/common/LocatorList.hpp>
#include <xmlparser/xmlutils/FlowControllerDescriptor.hpp>
#include <deps/common/GuidPrefix_t.hpp>
#include <xmlparser/xmlutils/PortParameters.h>
#include <deps/common/fixed_size_string.hpp>
#include <deps/common/ResourceManagement.h>

#include <deps/common/ExternalLocators.hpp>
#include <xmlparser/attributes/XMLRTPSParticipantAllocationAttributes.hpp>
#include <deps/common/PropertyPolicy.h>
#include <xmlparser/transport_descriptors/transport_descriptor_interface.h>

#include <cstdint>
#include <string>
#include <memory>
#include <sstream>

namespace vbsutil {
namespace xmlparser {

struct ParticipantType {
    static constexpr const char* SIMPLE = "SIMPLE";
    static constexpr const char* SERVER = "SERVER";
    static constexpr const char* CLIENT = "CLIENT";
    static constexpr const char* SUPER_CLIENT = "SUPER_CLIENT";
    static constexpr const char* BACKUP = "BACKUP";
    static constexpr const char* EXTERNAL = "EXTERNAL";
    static constexpr const char* NONE = "NONE";
    static constexpr const char* UNKNOWN = "UNKNOWN";
};

//! PDP subclass choice
typedef enum DiscoveryProtocol {
    NONE,
    /*!<
        NO discovery whatsoever would be used.
        Publisher and Subscriber defined with the same topic name would NOT be linked.
        All matching must be done manually through the addReaderLocator, addReaderProxy,
       addWriterProxy methods.
     */
    SIMPLE,
    /*!<
    Discovery works according to 'The Real-time Publish-Subscribe Protocol(RTPS) DDS
    Interoperability Wire Protocol Specification'
 */
    EXTERNAL,
    /*!<
        A user defined PDP subclass object must be provided in the attributes that deals with the
       discovery. Framework is not responsible of this object lifetime.
     */
    CLIENT,      /*!< The participant will behave as a client concerning discovery operation.
                      Server locators should be specified as attributes. */
    SERVER,      /*!< The participant will behave as a server concerning discovery operation.
                      Discovery operation is volatile (discovery handshake must take place if shutdown).
                  */
    BACKUP,      /*!< The participant will behave as a server concerning discovery operation.
                     Discovery operation persist on a file (discovery handshake wouldn't repeat if
                    shutdown). */
    SUPER_CLIENT /*!< The participant will behave as a client concerning all internal behaviour.
                    Remote servers will treat it as a server and will share every discovery
                    information. */
} DiscoveryProtocol_t;

inline std::ostream& operator<<(std::ostream& output, const DiscoveryProtocol& discovery_protocol) {
    switch (discovery_protocol) {
        case DiscoveryProtocol::NONE:
            output << xmlparser::ParticipantType::NONE;
            break;
        case DiscoveryProtocol::SIMPLE:
            output << xmlparser::ParticipantType::SIMPLE;
            break;
        case DiscoveryProtocol::EXTERNAL:
            output << xmlparser::ParticipantType::EXTERNAL;
            break;
        case DiscoveryProtocol::CLIENT:
            output << xmlparser::ParticipantType::CLIENT;
            break;
        case DiscoveryProtocol::SUPER_CLIENT:
            output << xmlparser::ParticipantType::SUPER_CLIENT;
            break;
        case DiscoveryProtocol::SERVER:
            output << xmlparser::ParticipantType::SERVER;
            break;
        case DiscoveryProtocol::BACKUP:
            output << xmlparser::ParticipantType::BACKUP;
            break;
        default:
            output << xmlparser::ParticipantType::UNKNOWN;
    }
    return output;
}

//! Filtering flags when discovering participants
typedef enum ParticipantFilteringFlags : uint32_t {
    NO_FILTER = 0,
    FILTER_DIFFERENT_HOST = 0x1,
    FILTER_DIFFERENT_PROCESS = 0x2,
    FILTER_SAME_PROCESS = 0x4
} ParticipantFilteringFlags_t;

#define BUILTIN_DATA_MAX_SIZE 512U

/**
 * Class SimpleEDPAttributes, to define the attributes of the Simple Endpoint Discovery Protocol.
 * @ingroup RTPS_ATTRIBUTES_MODULE
 */
class SimpleEDPAttributes {
 public:
    //! Default value true.
    bool use_PublicationWriterANDSubscriptionReader;

    //! Default value true.
    bool use_PublicationReaderANDSubscriptionWriter;

#if HAVE_SECURITY
    bool enable_builtin_secure_publications_writer_and_subscriptions_reader;

    bool enable_builtin_secure_subscriptions_writer_and_publications_reader;
#endif  // if HAVE_SECURITY

    SimpleEDPAttributes()
        : use_PublicationWriterANDSubscriptionReader(true),
          use_PublicationReaderANDSubscriptionWriter(true)
#if HAVE_SECURITY
          ,
          enable_builtin_secure_publications_writer_and_subscriptions_reader(true),
          enable_builtin_secure_subscriptions_writer_and_publications_reader(true)
#endif  // if HAVE_SECURITY
    {
    }

    bool operator==(const SimpleEDPAttributes& b) const {
        return (this->use_PublicationWriterANDSubscriptionReader == b.use_PublicationWriterANDSubscriptionReader) &&
#if HAVE_SECURITY
               (this->enable_builtin_secure_publications_writer_and_subscriptions_reader ==
                b.enable_builtin_secure_publications_writer_and_subscriptions_reader) &&
               (this->enable_builtin_secure_subscriptions_writer_and_publications_reader ==
                b.enable_builtin_secure_subscriptions_writer_and_publications_reader) &&
#endif  // if HAVE_SECURITY
               (this->use_PublicationReaderANDSubscriptionWriter == b.use_PublicationReaderANDSubscriptionWriter);
    }
};
/**
 * Struct InitialAnnouncementConfig defines the behavior of the RTPSParticipant initial
 * announcements.
 * @ingroup RTPS_ATTRIBUTES_MODULE
 */
struct InitialAnnouncementConfig {
    /// Number of initial announcements with specific period (default 5)
    uint32_t count = 5u;

    /// Specific period for initial announcements (default 50ms)
    Duration_t period = {0, 50000000u};

    bool operator==(const InitialAnnouncementConfig& b) const { return (count == b.count) && (period == b.period); }
};

/**
 * Class DiscoverySettings, to define the attributes of the several discovery protocols available
 * @ingroup RTPS_ATTRIBUTES_MODULE
 */
class DiscoverySettings {
 public:
    //! Chosen discovery protocol
    DiscoveryProtocol_t discoveryProtocol = DiscoveryProtocol_t::CLIENT;

    /**
   * If set to true, SimpleEDP would be used.
   */
    bool use_SIMPLE_EndpointDiscoveryProtocol = true;

    /**
   * If set to true, StaticEDP based on an XML file would be implemented.
   * The XML filename must be provided.
   */
    bool use_STATIC_EndpointDiscoveryProtocol = false;

    /**
     * Lease Duration of the RTPSParticipant,
     * indicating how much time remote RTPSParticipants should consider this RTPSParticipant alive.
     */
    Duration_t leaseDuration = {10, 0U};

    /**
     * The period for the RTPSParticipant to send its Discovery Message to all other discovered
     * RTPSParticipants as well as to all Multicast ports.
     */
    Duration_t leaseDuration_announcementperiod = {3, 0U};

    //! Initial announcements configuration
    InitialAnnouncementConfig initial_announcements;

    //! Attributes of the SimpleEDP protocol
    SimpleEDPAttributes m_simpleEDP;

    /**
     * The period for the RTPSParticipant to:
     *  send its Discovery Message to its servers
     *  check for EDP endpoints matching
     */
    Duration_t discoveryServer_client_syncperiod = {0, 200U * 1000000U};  // 200 milliseconds

    //! Discovery Server initial connections, needed if `discoveryProtocol` = CLIENT | SUPER_CLIENT | SERVER | BACKUP
    LocatorList m_DiscoveryServers;

    //! Vector to store domain IDs
    std::vector<uint16_t> domainIDs;

    //! Filtering participants out depending on location
    ParticipantFilteringFlags_t ignoreParticipantFlags = ParticipantFilteringFlags::NO_FILTER;

    bool is_compatible = false;

    DiscoverySettings() = default;

    bool operator==(const DiscoverySettings& b) const {
        return (this->discoveryProtocol == b.discoveryProtocol) &&
               (this->use_SIMPLE_EndpointDiscoveryProtocol == b.use_SIMPLE_EndpointDiscoveryProtocol) &&
               (this->use_STATIC_EndpointDiscoveryProtocol == b.use_STATIC_EndpointDiscoveryProtocol) &&
               (this->leaseDuration == b.leaseDuration) &&
               (this->leaseDuration_announcementperiod == b.leaseDuration_announcementperiod) &&
               (this->initial_announcements == b.initial_announcements) && (this->m_simpleEDP == b.m_simpleEDP) &&
               (this->static_edp_xml_config_ == b.static_edp_xml_config_) &&
               (this->ignoreParticipantFlags == b.ignoreParticipantFlags);
    }

    /**
     * Set the static endpoint XML configuration.
     * @param str URI specifying the static endpoint XML configuration.
     * The string could contain a filename (file://) or the XML content directly (data://).
     */
    void static_edp_xml_config(const char* str) { static_edp_xml_config_ = ((str == nullptr) ? "" : str); }

    /**
     * Get the static endpoint XML configuration.
     * @return URI specifying the static endpoint XML configuration.
     * The string could contain a filename (file://) or the XML content directly (data://).
     */
    const char* static_edp_xml_config() const { return static_edp_xml_config_.c_str(); }

 private:
    //! URI specifying the static EDP XML configuration, only necessary if use_STATIC_EndpointDiscoveryProtocol=true
    //! This string could contain a filename or the XML content directly.
    std::string static_edp_xml_config_ = "";
};

/**
 * Service settings.
 */

class LookupSettings {
 public:
    //! Indicates to use the Lookup Service client endpoints
    bool use_client = false;

    //! Indicates to use the Lookup Service server endpoints
    bool use_server = true;
};

/**
 * Class BuiltinAttributes, to define the behavior of the RTPSParticipant builtin protocols.
 * @ingroup RTPS_ATTRIBUTES_MODULE
 */
class BuiltinAttributes {
 public:
    //! Discovery protocol related attributes
    DiscoverySettings discovery_config;

    //! Indicates to use the WriterLiveliness protocol.
    bool use_WriterLivelinessProtocol = true;

    //! Lookup Service settings
    LookupSettings lookup_config;

    //! Metatraffic Unicast Locator List
    LocatorList_t metatrafficUnicastLocatorList;

    //! Metatraffic Multicast Locator List.
    LocatorList_t metatrafficMulticastLocatorList;

    //! Raw Multicast Locator List.
    LocatorList_t rawMulticastLocatorList;

    //! The collection of external locators to use for communication on metatraffic topics.
    vbsutil::xmlparser::ExternalLocators metatraffic_external_unicast_locators;

    //! Initial peers.
    LocatorList_t initialPeersList;

    //! Memory policy for builtin readers
    MemoryManagementPolicy_t readerHistoryMemoryPolicy = MemoryManagementPolicy_t::AUTOMATIC_MEMORY_MODE;

    //! Maximum payload size for builtin readers
    uint32_t readerPayloadSize = BUILTIN_DATA_MAX_SIZE;

    //! Memory policy for builtin writers
    MemoryManagementPolicy_t writerHistoryMemoryPolicy = MemoryManagementPolicy_t::AUTOMATIC_MEMORY_MODE;

    //! Maximum payload size for builtin writers
    uint32_t writerPayloadSize = BUILTIN_DATA_MAX_SIZE;

    //! Mutation tries if the port is being used.
    uint32_t mutation_tries = 100u;

    //! Set to true to avoid multicast traffic on builtin endpoints
    bool avoid_builtin_multicast = true;

    BuiltinAttributes() = default;

    virtual ~BuiltinAttributes() = default;

    bool operator==(const BuiltinAttributes& b) const {
        return (this->discovery_config == b.discovery_config) &&
               (this->use_WriterLivelinessProtocol == b.use_WriterLivelinessProtocol) &&
               (lookup_config.use_client == b.lookup_config.use_client) &&
               (lookup_config.use_server == b.lookup_config.use_server) &&
               (this->metatrafficUnicastLocatorList == b.metatrafficUnicastLocatorList) &&
               (this->metatrafficMulticastLocatorList == b.metatrafficMulticastLocatorList) &&
               (this->metatraffic_external_unicast_locators == b.metatraffic_external_unicast_locators) &&
               (this->initialPeersList == b.initialPeersList) &&
               (this->readerHistoryMemoryPolicy == b.readerHistoryMemoryPolicy) &&
               (this->readerPayloadSize == b.readerPayloadSize) &&
               (this->writerHistoryMemoryPolicy == b.writerHistoryMemoryPolicy) &&
               (this->writerPayloadSize == b.writerPayloadSize) && (this->mutation_tries == b.mutation_tries) &&
               (this->avoid_builtin_multicast == b.avoid_builtin_multicast);
    }
};

class RTPSParticipantAttributes {
    using FlowControllerDescriptorList = std::vector<std::shared_ptr<xmlparser::FlowControllerDescriptor>>;

 public:
    RTPSParticipantAttributes() {
        setName("RTPSParticipant");
        sendSocketBufferSize = 0U;
        listenSocketBufferSize = 0U;
        participantID = -1;
        useBuiltinTransports = true;
        transport_type = TRANSPORT_TYPE_AUTO;
    }

    virtual ~RTPSParticipantAttributes() {}

    bool operator==(const RTPSParticipantAttributes& b) const {
        return (this->name == b.name) && (this->defaultUnicastLocatorList == b.defaultUnicastLocatorList) &&
               (this->defaultMulticastLocatorList == b.defaultMulticastLocatorList) &&
               (this->default_external_unicast_locators == b.default_external_unicast_locators) &&
               (this->ignore_non_matching_locators == b.ignore_non_matching_locators) &&
               (this->sendSocketBufferSize == b.sendSocketBufferSize) &&
               (this->listenSocketBufferSize == b.listenSocketBufferSize) && (this->builtin == b.builtin) &&
               (this->port == b.port) && (this->participantID == b.participantID) &&
               (this->useBuiltinTransports == b.useBuiltinTransports) && (this->properties == b.properties) &&
               (this->prefix == b.prefix) && (this->flow_controllers == b.flow_controllers);
    }

    /**
     * Default list of Unicast Locators to be used for any Endpoint defined inside this
     * RTPSParticipant in the case that it was defined with NO UnicastLocators. At least ONE locator
     * should be included in this list.
     */
    LocatorList_t defaultUnicastLocatorList;

    /**
     * Default list of Multicast Locators to be used for any Endpoint defined inside this
     * RTPSParticipant in the case that it was defined with NO MulticastLocators. This is usually
     * left empty.
     */
    LocatorList_t defaultMulticastLocatorList;

    /**
     * The collection of external locators to use for communication on user created topics.
     */
    vbsutil::xmlparser::ExternalLocators default_external_unicast_locators;

    /**
     * Whether locators that don't match with the announced locators should be kept.
     */
    bool ignore_non_matching_locators = false;

    /*!
     * @brief Send socket buffer size for the send resource. Zero value indicates to use default
     * system buffer size. Default value: 0.
     */
    uint32_t sendSocketBufferSize;

    /*! Listen socket buffer for all listen resources. Zero value indicates to use default system
     * buffer size. Default value: 0.
     */
    uint32_t listenSocketBufferSize;

    //! Optionally allows user to define the GuidPrefix_t
    GuidPrefix_t prefix;

    RTPS_DllAPI inline bool ReadguidPrefix(const char* pfx) { return bool(std::istringstream(pfx) >> prefix); }

    //! Builtin parameters.
    BuiltinAttributes builtin;

    //! Port Parameters
    PortParameters port;

    //! Participant ID
    int32_t participantID;

    //! User defined transports to use alongside or in place of builtins.
    std::vector<std::shared_ptr<xmlparser::TransportDescriptorInterfacePart>> userTransportsDSF;
    std::vector<std::string> userTransports;

    //! Set as false to disable the default UDPv4 implementation.
    bool useBuiltinTransports;

    vbsutil::xmlparser::TransportTypeKind transport_type;

    //! Holds allocation limits affecting collections managed by a participant.
    RTPSParticipantAllocationAttributes allocation;

    //! Property policies
    PropertyPolicy properties;

    //! Set the name of the participant.
    inline void setName(const char* nam) { name = nam; }

    //! Get the name of the participant.
    inline const char* getName() const { return name.c_str(); }

    //! Flow controllers.
    FlowControllerDescriptorList flow_controllers;

 private:
    //! Name of the participant.
    string_255 name;
};

}  // namespace xmlparser
}  // namespace vbsutil

#endif
